工厂模式是一种较为常见的设计模式,主要用于对象的创建。
What
当我们需要处理对象间的依赖时,如当某接口的实现类对象被依赖,通常需要在代码中利用new创建对象,而其实依赖方只想调用其对象的一些方法,而不关心该对象的创建过程。
而且当代码中有多处创建了此类对象,一旦构造方法发生了变化,则需要将多处构造方法同时修改,重复劳动。
工厂模式解决的主要就是上述两个问题:
- 将调用方与资源解耦,调用方将资源的初始化统一交给工厂,将资源的创建与使用分离。
- 避免重复进行复杂对象的初始化。
如下,我们以内存产品作为资源为例。1
2
3
4
5
6
7
8
9
10
11interface RAM {}
abstract class RAM8G implements RAM {
public RAM8G() {
System.out.println("building 8G RAM");
}
}
class RAM16G implements RAM {
public RAM16G() {
System.out.println("building 16G RAM");
}
}
简单/静态工厂
静态工厂的思路是一个工厂搞定全部类似对象的请求,实现非常简单,直接将对象的创建交给工厂,由参数来区分调用方具体需要的对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class StaticFactory {
private final static int SIZE_8G = 8;
private final static int SIZE_16G = 16;
public RAM createRAM(int size) {
switch(size) {
case SIZE_8G: return new RAM8G();
case SIZE_16G : return new RAM16G();
default: return null;
}
}
}
public class StaticFactoryTest {
public static void main(String[] args) {
StaticFactory factory = new StaticFactory();
RAM _8g = factory.createRAM(8);
RAM _16g = factory.createRAM(16);
}
}
也可以用静态的方式去写工厂,连工厂的实例化都省了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class StaticFactory {
...
public static RAM createRAM(int size) {
switch(size) {
case SIZE_8G: return new RAM8G();
case SIZE_16G : return new RAM16G();
default: return null;
}
}
}
public class StaticFactoryTest {
public static void main(String[] args) {
RAM _8g = StaticFactory.createRAM(8);
RAM _16g = StaticFactory.createRAM(16);
}
}
问题
- 当需要扩展产品时,如加一个32G的内存,需修改工厂类的
createRAM
方法,不符合开闭原则中的“对修改关闭”原则。 - 大量逻辑堆积在工厂类,难以支持创建需要额外参数的产品。
工厂方法
工厂方法模式为不同产品的相同方法创建不同的工厂,可以理解为对简单工厂的抽象,而且更加直观。
定义
根据GoF的定义,工厂方法使用继承,由子类来完成实例的初始化。
实现
针对简单工厂,其实可以适当做些改进,或许我们可以使用更直观的方式来替换switch
:1
2
3
4
5
6
7
8
9
10
11
12
13
14class StaticFactory {
public static RAM create8GRAM() {
return RAM8G();
}
public static RAM create16GRAM() {
return RAM16G();
}
}
public class StaticFactoryTest {
public static void main(String[] args) {
RAM _8g = StaticFactory.create8GRAM();
RAM _16g = StaticFactory.create16GRAM();
}
}
上述改进是基于方法名来区分不同产品,而工厂方法则用工厂类名来区分。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23interface RAMFactory {
RAM createRAM();
}
class RAM8GFactory implements RAMFactory {
public RAM createRAM() {
return new RAM8G();
}
}
class RAM16GFactory implements RAMFactory {
public RAM createRAM() {
return new RAM16G();
}
}
public class FactoryMethodTest {
public static void main(String[] args) {
RAMFactory factory8G = new RAM8GFactory();
RAMFactory factory16G = new RAM16GFactory();
RAM _8g = factory8G.createRAM();
RAM _16g = factory16G.createRAM();
}
}
问题
当我们需扩展产品时,如新增一个32G的产品,需新增完整的工厂类实现,因此如果产品很多时会有很多的工厂类。
抽象工厂
当一个大类型的产品下出现多维度的细分,如8G内存可以分为DDR3和DDR4,形成等级结构,此时有产品簇的概念。
产品簇
不同等级结构中功能关联的产品,如8G内存及16G内存分为DDR3和DDR4,8G DDR3和16G DDR3就是一个产品簇的。
抽象工厂的思路是将不变的属性(分类)作为工厂。如果产品全属于单一等级结构,则退化为工厂方法模式。
定义
根据GoF的说法,在抽象工厂模式中,使用组合的方式去委派实例的创建任务。
实现
1 | // 产品,出现了新的维度 |
比较
工厂方法模式和抽象工厂模式的调用方式显得比简单工厂更直观,体现在使用方法名或类名来区分不同的产品,而不是通过调用方传的参数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40// 简单工厂
public class FooFactory {
public Foo(int withBars) {
... // 根据参数switch
}
}
// 工厂方法
public class FooFactory {
public Foo withoutBar();
public Foo withOneBar();
public Foo withTwoBars();
}
// 抽象工厂
public class NoBarFooFactory {
public Foo withBaz();
public Foo withoutBaz();
}
public class OneBarFooFactory {
public Foo withBaz();
public Foo withoutBaz();
}
public class TwoBarsFooFactory {
public Foo withBaz();
public Foo withoutBaz();
}
// 调用简单工厂
ff.foo(0);
ff.foo(1);
ff.foo(2);
// 调用工厂方法
ff.withoutBar();
ff.withOneBar();
ff.withTwoBars();
// 调用抽象工厂
noBarFF.withBaz();
noBarFF.withoutBaz();
oneBarFF.withBaz();
oneBarFF.withoutBaz();
twoBarsFF.withBaz();
twoBarsFF.withoutBaz();